package com.jeaven.rtda.heap;

import com.jeaven.classfile.attribute.Exception;
import com.jeaven.classfile.attribute.ExceptionTable;
import com.jeaven.classfile.attribute.Line;
import com.jeaven.classfile.attribute.LineNumerTable;
import com.jeaven.instruction.Instruction;
import com.jeaven.utils.Utils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class KMethod {
    public final int accessFlags;
    public final String name;
    public final String descriptor;

    public final int maxStacks;
    public final int maxLocals;
    public final Map<Integer, Instruction> instructionMap;
    public final ExceptionTable exceptionTable;
    public final LineNumerTable lineNumberTable;

    public KClass clazz;

    public KMethod(int accessFlags, String name, String descriptor, int maxStacks, int maxLocals,
                   Map<Integer, Instruction> instructionMap, ExceptionTable exceptionTable,
                   LineNumerTable lineNumberTable) {
        this.accessFlags = accessFlags;
        this.name = name;
        this.descriptor = descriptor;
        this.maxStacks = maxStacks;
        this.maxLocals = maxLocals;
        this.instructionMap = instructionMap;
        this.exceptionTable = exceptionTable;
        this.lineNumberTable = lineNumberTable;
    }

    public String getReturnType() {
        return this.descriptor.substring(this.descriptor.indexOf(")") + 1);
    }

    public List<String> getArgs() {  // 返回方法参数类型
        return Utils.parseMethodDescriptor(descriptor);
    }

    public int getArgSlotSize() {
        List<String> args = Utils.parseMethodDescriptor(descriptor);
        int slotSize = 0;
        List<String> oneSlot = Arrays.asList("V", "Z", "B", "C", "S", "I", "F");
        List<String> twoSlot = Arrays.asList("J", "D");
        for(String arg : args) {
            if(oneSlot.contains(arg) || arg.contains("L") || arg.contains("[")) {
                slotSize += 1;
            }
            if(twoSlot.contains(arg)) {
                slotSize += 2;
            }
        }
        return slotSize;
    }

    @Override
    public String toString() {
        return "KMethod{" +
                "accessFlags=" + accessFlags +
                ", name='" + name + '\'' +
                ", descriptor='" + descriptor + '\'' +
                ", maxStacks=" + maxStacks +
                ", maxLocals=" + maxLocals +
                ", instructionMap=" + instructionMap +
                ", clazz=" + clazz.name +
                '}';
    }

    public boolean isNative() {
        return (this.accessFlags & 0x0100) != 0;
    }

    public boolean isStatic() {
        return (this.accessFlags & 0x0008) != 0;
    }

    public String nativeMethodKey() {
        return Utils.generateNativeMethodKey(this);
    }

    public Integer getHandlerPc(Integer pc, String name) {
        for (Exception exception : this.exceptionTable.exceptions) {
            if (exception.clazz == null || Objects.equals(exception.clazz, name)) {
                if (pc >= exception.startPc && pc < exception.endPc) {
                    return exception.handlerPc;
                }
            }
        }
        return null;
    }

    public int getLine(int pc) {
        int ret = 0;
        for (Line line : this.lineNumberTable.lines) {
            if (line.startPC <= pc) {
                ret = line.lineNum;
            } else {
                break;
            }
        }
        return ret;
    }

}
